qapply <- function(X, FUN, fargs=NULL, commonData=NULL, workDir=getwd(),
                   nCores=as.numeric(.getResources()), 
                   nPerPacket=NULL,
                   tag=deparse(substitute(X)), internalize=TRUE,
                   clearWd=TRUE, parSeed=NULL)
{
  FUN <- match.fun(FUN)
  results <- NULL
  if (!is.vector(X) || is.object(X))
    X <- as.list(X)
  if (is.null(nPerPacket)) nPerPacket=.nPerPacket(nr=length(X), nc=nCores)
  nPerPacket <- nPerPacket[ !is.na(nPerPacket) & !is.null(nPerPacket) & (nPerPacket!=0) ]
  if (sum(nPerPacket) != length(X)){
    print("Specify nPerPacket as an array whose sum matches nrow(X)")
  }else{
    ## Prevent errors from sloppy tag usage:
    if(length(list.dirs(file.path(workDir, "out", tag))) > 0) qclearWd(workDir, tag)
    oldDir <- getwd()
    setwd(workDir)
    commonEnv <- list( commonData=commonData, X=X, FUN=FUN, fargs=eval(fargs), libPaths = .libPaths(), packages=(.packages()) )
    save( commonEnv, file=paste(tag, "common.Rdata", sep="-"))
    funScript <- system.file("qcontrol.r", package="qapply", mustWork=TRUE)
    file.copy(funScript, "qcontrol.r")
    if( is.null(parSeed) ){ parSeed <- rep(1111, 6) }
    for(i in 1:length(nPerPacket)){
      rowBegin <- ifelse( i==1, 1, sum(nPerPacket[1:(i-1)])+1 )
      rowEnd <- sum(nPerPacket[1:i])
      qsub("R --no-save", tag=tag, rowBegin=rowBegin, rowEnd=rowEnd,
           cmdArgs= c("--args", tag, rowBegin, rowEnd, i, length(nPerPacket), parSeed, "< qcontrol.r"))
    }
    if(internalize){
      qwait(tag)
      results <- qinternalize(file.path("out", tag))
      if(clearWd){
        qclearWd(workDir,tag)
      }
    }
  }
  setwd(oldDir)
  results
}
